package factorytest.iflytek.com.einkiflytekfactorytest.common;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import factorytest.iflytek.com.einkiflytekfactorytest.config.EinkProperty;
import factorytest.iflytek.com.einkiflytekfactorytest.utils.DebugLog;
import factorytest.iflytek.com.einkiflytekfactorytest.utils.ReflectUtils;
import factorytest.iflytek.com.einkiflytekfactorytest.utils.executor.AppExecutors;

public class RecordAndPlayThread extends Thread {


    private static final String RECORDER_FILE_PATH_PREFIX = FTUtils.getFTStorageDirectory() + File.separator;

    private volatile boolean keepAlive = true;

    private final int SAMPLE_RATE_IN_HZ = 16000;
    private int IN_FORMAT = AudioFormat.ENCODING_PCM_16BIT;

    private AudioRecord mAudioRecord;
    private AudioTrack mAudioTrack;

    private final boolean mUseNewInterface;
    private final Context mContext;
    private final String RECORDER_FILE_PATH;
    private final int streamType;

    public RecordAndPlayThread(String name, Context ctx, int streamType, boolean isES7210) {
        super(name);
        mContext = ctx;

        this.streamType = streamType;

        IN_FORMAT = isES7210 ? AudioFormat.ENCODING_PCM_FLOAT : AudioFormat.ENCODING_PCM_16BIT;

        RECORDER_FILE_PATH = RECORDER_FILE_PATH_PREFIX + name + ".pcm";

        AudioManager audioManager = mContext.getSystemService(AudioManager.class);
        mUseNewInterface = ReflectUtils.getMethodWithNoException(audioManager, "stopBTScoAndResetInput") != null;
    }

    @Override
    public void run() {

        try {

            int sampleRate = SAMPLE_RATE_IN_HZ;

            int bufferSizeRec = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_STEREO, IN_FORMAT);
            int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

            DebugLog.e("bufferSizeRec = " + bufferSizeRec);

            if (mAudioRecord == null) {
                mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
                        AudioFormat.CHANNEL_IN_STEREO, IN_FORMAT, bufferSizeRec);
            }
            if (mUseNewInterface) {
                ReflectUtils.invokeMethodWithNoException(mAudioRecord, "startRecording", mContext);
                DebugLog.e("here to startRecording(ctx)");
            } else {
                mAudioRecord.startRecording();
            }

            int ratio = bufferSize / bufferSizeRec;
            byte[] buffer = new byte[(ratio + 1) * bufferSizeRec];

            AppExecutors.runOnIoThread(playRunnable);

            while (keepAlive) {

                int len = mAudioRecord.read(buffer, 0, buffer.length);

                if (len < AudioRecord.SUCCESS) {
                    DebugLog.e("recordAndPlay has error--------------");
                    break;
                }

                playQueue.put(ByteBuffer.wrap(buffer, 0, len));

            }

        }catch (Exception e) {

            DebugLog.e(e.getMessage());

        } finally {
            DebugLog.e("record finish..........................");

            byte[] buffer = new byte[1];
            playQueue.add(ByteBuffer.wrap(buffer, 0, buffer.length));
            if (mAudioRecord != null) {
                try {
                    mAudioRecord.stop();
                    mAudioRecord.release();
                    mAudioRecord = null;
                } catch (Exception ignored) {
                }
            }

        }

    }

    public void joinThread() {
        keepAlive = false;
        while (isAlive()) {
            try {
                join();
            } catch (InterruptedException e) {
                // Ignore.
            }
        }
    }

    ///////////////

    private BlockingQueue<ByteBuffer> playQueue = new LinkedBlockingQueue<>(128);

    private Runnable playRunnable = new Runnable() {
        @Override
        public void run() {
            BufferedOutputStream bos = null;

            try {

                int sampleRate = SAMPLE_RATE_IN_HZ;

                int bufferSizeRec = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_STEREO, IN_FORMAT);
                int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

                DebugLog.e("bufferSize = " + bufferSize);

                int ratio = bufferSize / bufferSizeRec;
                int bufferSizeCommon = (ratio + 1) * bufferSizeRec;
                if (mAudioTrack == null) {
                    mAudioTrack = new AudioTrack(streamType, sampleRate,
                            AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeCommon, AudioTrack.MODE_STREAM);
                } else {
                    mAudioTrack.stop();
                }

                mAudioTrack.play();

                File file = new File(RECORDER_FILE_PATH);
                if (file.exists()) {
                    file.delete();
                }
                bos = new BufferedOutputStream(new FileOutputStream(file));

                while (keepAlive) {

                    ByteBuffer byteBuffer = playQueue.take();
                    if (byteBuffer != null && byteBuffer.array().length != 1) {

                        if (IN_FORMAT == AudioFormat.ENCODING_PCM_FLOAT) {
                            byte[] buffer = new byte[byteBuffer.array().length / 2];

                            for (int n = 0, i = 0; n < byteBuffer.array().length; ) {
                                n++;
                                n++;
                                buffer[i++] = byteBuffer.array()[n++];
                                buffer[i++] = byteBuffer.array()[n++];
                            }

                            mAudioTrack.write(buffer, 0, buffer.length);
                            buffer = null;
                        } else {
                            mAudioTrack.write(byteBuffer.array(), 0, byteBuffer.array().length);
                        }

                        bos.write(byteBuffer.array(), 0, byteBuffer.array().length);
                        bos.flush();
                        byteBuffer = null;
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {

                playQueue.clear();

                DebugLog.e("play and write file finish..........................");

                if (bos != null) {
                    try {
                        bos.flush();
                        bos.close();
                    } catch (IOException ignored) {
                    }
                }

                if (mAudioTrack != null) {
                    try {
                        mAudioTrack.stop();
                        mAudioTrack.flush();
                        mAudioTrack.release();
                        mAudioTrack = null;
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    };

}
